package minidb.index;

import static minidb.type.Const.*;
import minidb.buffer.Buffer;
import minidb.record.RID;

public class bTreeNode {
	int pageNum;
	int pos;
	final int PARENT = INT_SIZE*3;
	final int TOTAL = INT_SIZE*4;
	final int MARK = INT_SIZE * 5;
	final int SLOT = INT_SIZE * 3;
	final int NODE_MAX = 83;
	
	public bTreeNode(int pageNum){
		this.pageNum = pageNum;
		this.pos = MARK + INT_SIZE;
	}
	
	public Node next(){
		Buffer buf = bufpool.pin(pageNum);
		int total = buf.getInt(TOTAL);
		if(pos + SLOT > INT_SIZE * 6 + total*SLOT){
			pos+=SLOT;
			bufpool.unpin(buf);
			return null;
		}
		Node r = new Node(buf,pos);
		pos+=SLOT;
		bufpool.unpin(buf);
		return r;
	}
	
	public boolean hasNext(){
		Buffer buf = bufpool.pin(pageNum);
		int total = buf.getInt(TOTAL);
		if(pos + SLOT > INT_SIZE * 6 + SLOT * total ){
			bufpool.unpin(buf);
			return false;
		}
		bufpool.unpin(buf);
		return true;
	}
	public int pageNum(){ return this.pageNum; }
	
	public int parent(){  
		Buffer buf = bufpool.pin(pageNum);
		int result = buf.getInt(PARENT); 
		bufpool.unpin(buf);
		return result;
	}
	
	public void toLast(){
		Buffer buf = bufpool.pin(pageNum);
		int total = buf.getInt(TOTAL);
		this.pos = INT_SIZE*6+total*SLOT;
		bufpool.unpin(buf);
	}
	
	public int replace(RID r){
		Buffer buf = bufpool.pin(pageNum);
		int total = buf.getInt(TOTAL);
		buf.putInt(pos, r.pageNum());
		buf.putInt(pos+INT_SIZE, r.rid());
		pos+=SLOT;
		bufpool.unpin(buf);
		return total;
	}
	
	public int replace(Node n){
		Buffer buf = bufpool.pin(pageNum);
		int total = buf.getInt(TOTAL);
		buf.putInt(pos, n.rid().pageNum());
		buf.putInt(pos+INT_SIZE, n.rid().rid());
		buf.putInt(pos+INT_SIZE*2, n.next());
		pos+=SLOT;
		bufpool.unpin(buf);
		return total;
	}
	
	public void prev(){
		pos-=SLOT;
	}
	
	public int pos(){
		return this.pos;
	}
	public void Rid(RID r){
		this.pageNum = r.pageNum();
		this.pos = r.rid();
	}

	public RID Rid(){
		return new RID(this.pageNum, this.pos);
	}
	
	public Node insert(Node r){
		Buffer buf = bufpool.pin(pageNum);
		int total = buf.getInt(TOTAL);
		int length = PAGE_SIZE - pos - SLOT;
		total++;
		buf.putInt(TOTAL, total);
		java.nio.ByteBuffer tmp = java.nio.ByteBuffer.allocateDirect(length);
		buf.getByte(pos, tmp, length);
		buf.putInt(pos, r.pageNum());
		buf.putInt(pos+INT_SIZE, r.pos());
		buf.putInt(pos+INT_SIZE*2, r.next());
		pos+=SLOT;
		buf.putByte(pos, tmp, length);
		if(total==NODE_MAX){
			length = INT_SIZE*123;
			tmp = java.nio.ByteBuffer.allocateDirect(PAGE_SIZE);
			buf.getByte(INT_SIZE+MARK, tmp, length);
			total = 41;
			int pageNum = buf.getInt(CURRENT_PAGE);
			int prePage = buf.getInt(PRE_PAGE);
			int newPage = dbFile.allocPage();
			int parentPage = buf.getInt(PARENT);
			buf.putInt(PRE_PAGE, newPage);
			java.nio.ByteBuffer b = java.nio.ByteBuffer.allocateDirect(PAGE_SIZE);
			buf.getByte(length+INT_SIZE*6, b,length+INT_SIZE*3);
			buf.putInt(TOTAL, total+1);
			buf.putByte(MARK+INT_SIZE, b, length+INT_SIZE*3);
			if(prePage!=0){
				bufpool.unpin(buf);
				buf = bufpool.pin(prePage);
				buf.putInt(NEXT_PAGE, newPage);
			}
			Rid(new RID(newPage, INT_SIZE+MARK));
			bufpool.unpin(buf);
			buf = bufpool.pin(this.pageNum);
			buf.putInt(PRE_PAGE, prePage);
			buf.putInt(CURRENT_PAGE, newPage);
			buf.putInt(NEXT_PAGE, pageNum);
			buf.putInt(TOTAL, total);
			buf.putInt(PARENT, parentPage);
			buf.putInt(MARK, 0);
			buf.putByte(INT_SIZE * 6, tmp, length);
			Node node = new Node(buf.getInt(length+INT_SIZE*3),buf.getInt(length+INT_SIZE*4),newPage);
			bufpool.unpin(buf);
			return node;
		}
		bufpool.unpin(buf);
		return null;
	}
	
	public int delete(){
		Buffer buf = bufpool.pin(pageNum);
		int total = buf.getInt(TOTAL);
		int length = PAGE_SIZE - pos - SLOT;
		total--;
		buf.putInt(TOTAL, total);
		java.nio.ByteBuffer tmp = java.nio.ByteBuffer.allocateDirect(length);
		buf.getByte(pos+SLOT, tmp, length);
		buf.putByte(pos, tmp, length);
		return total;
	}
	
}
